/************************************************************************
*
* Copyright (c) 2014-2025 Barbara Geller & Ansel Sermersheim
*
* Copyright (c) 1997-2014 Dimitri van Heesch
*
* DoxyPress is free software. You can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* DoxyPress is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* Documents produced by DoxyPress are derivative works derived from the
* input used in their production; they are not affected by this license.
*
* https://www.gnu.org/licenses/
*
*************************************************************************/

#include <rtfgen.h>

#include <config.h>
#include <diagram.h>
#include <docparser.h>
#include <dot.h>
#include <doxy_build_info.h>
#include <doxy_globals.h>
#include <groupdef.h>
#include <language.h>
#include <message.h>
#include <rtfdocvisitor.h>
#include <rtfstyle.h>
#include <util.h>

#include <QDir>
#include <QRegularExpression>
#include <QTextCodec>
#include <QTextStream>

#include <stdlib.h>

#define DBG_RTF(x)

static QString dateToRTFDateString()
{
   static const QDateTime dt = Doxy_Globals::dateTime;

   QString retval;

   retval = QString("\\yr%1\\mo%2\\dy%3\\hr%4\\min%5\\sec%6")
                  .formatArg(dt.date().year()).formatArg(dt.date().month()).formatArg(dt.date().day())
                  .formatArg(dt.time().hour()).formatArg(dt.time().minute()).formatArg(dt.time().second());

   return retval;
}

RTFGenerator::RTFGenerator() : OutputGenerator()
{
   m_outputDir  = Config::getString("rtf-output");
   m_prettyCode = Config::getBool("rtf-source-code");

   m_col       = 0;
   m_numCols   = 0;
   m_listLevel = 0;

   m_bstartedBody     = false;
   m_omitParagraph    = false;
   m_doxyCodeLineOpen = false;
}

//void RTFGenerator::append(const OutputGenerator *g)
//{
//  t << g->getContents();
//  col+=((RTFGenerator *)g)->col;
//  //insideTabbing=insideTabbing || ((RTFGenerator *)g)->insideTabbing;
//  m_listLevel=((RTFGenerator *)g)->m_listLevel;
//  m_omitParagraph=((RTFGenerator *)g)->m_omitParagraph;
//}

//OutputGenerator *RTFGenerator::copy()
//{
//  RTFGenerator *result = new RTFGenerator;
//  //result->insideTabbing=insideTabbing;
//  result->m_listLevel=m_listLevel;
//  result->m_omitParagraph=m_omitParagraph;
//  return result;
//}

void RTFGenerator::writeStyleSheetFile(QFile &file)
{
   QTextStream t_stream(&file);

   t_stream << "# Generated by DoxyPress " << versionString << "\n\n";
   t_stream << "# This file describes styles used for generating RTF output.\n";
   t_stream << "# All text after a hash (#) is considered a comment and will be ignored.\n";
   t_stream << "# Remove a hash to activate a line.\n\n";

   for (int i = 0; ! rtf_Style_Default[i].style_reference.isEmpty(); ++i ) {

      t_stream << "# " << rtf_Style_Default[i].style_name << " = "
        << rtf_Style_Default[i].style_reference
        << rtf_Style_Default[i].style_definition << endl;
   }
}

void RTFGenerator::writeExtensionsFile(QFile &file)
{
   QTextStream t_stream(&file);

   t_stream << "# Generated by DoxyPress " << versionString << "\n\n";
   t_stream << "# This file describes extensions used for generating RTF output.\n";
   t_stream << "# All text after a hash (#) is considered a comment and will be ignored.\n";
   t_stream << "# Remove a hash to activate a line.\n\n";

   t_stream << "# Overrides the project title.\n";

   t_stream << "#Title           = \n\n";

   t_stream << "# Name of the company that produced this document.\n";
   t_stream << "#Company         = \n\n";

   t_stream << "# Filename of a company or project logo.\n";
   t_stream << "#LogoFilename    = \n\n";

   t_stream << "# Author of the document.\n";
   t_stream << "#Author          = \n\n";

   t_stream << "# Type of document (e.g. Design Specification, User Manual, etc.).\n";
   t_stream << "#DocumentType    = \n\n";

   t_stream << "# Document tracking number.\n";
   t_stream << "#DocumentId      = \n\n";

   t_stream << "# Name of the author's manager.\n";
   t_stream << "# This field is not displayed in the document itself, but it is \n";
   t_stream << "# available in the information block of the rtf file.  In Microsoft \n";
   t_stream << "# Word, it is available under File:Properties.\n";
   t_stream << "#Manager         = \n\n";

   t_stream << "# Subject of the document.\n";
   t_stream << "# This field is not displayed in the document itself, but it is \n";
   t_stream << "# available in the information block of the rtf file.  In Microsoft \n";
   t_stream << "# Word, it is available under File:Properties.\n";
   t_stream << "#Subject         = \n\n";

   t_stream << "# Comments regarding the document.\n";
   t_stream << "# This field is not displayed in the document itself, but it is \n";
   t_stream << "# available in the information block of the rtf file.  In Microsoft \n";
   t_stream << "# Word, it is available under File:Properties.\n";
   t_stream << "#Comments        = \n\n";

   t_stream << "# Keywords associated with the document.\n";
   t_stream << "# This field is not displayed in the document itself, but it is \n";
   t_stream << "# available in the information block of the rtf file.  In Microsoft \n";
   t_stream << "# Word, it is available under File:Properties.\n";
   t_stream << "#Keywords        = \n\n";
}

void RTFGenerator::init()
{
   static const QString rtfStyleSheetFile = Config::getString("rtf-stylesheet");
   static const QString rtfExtensionsFile = Config::getString("rtf-extension");

   QDir d(m_outputDir);

   if (! d.exists() && ! d.mkdir(m_outputDir)) {
      err("Unable to create output directory %s\n", csPrintable(m_outputDir));
      Doxy_Work::stopDoxyPress();
   }

   // first duplicate strings of rtf_Style_Default
   const struct Rtf_Style_Default *def = rtf_Style_Default;

   while (! def->style_reference.isEmpty()) {

      if (def->style_definition.isEmpty()) {
         err("RTF Style Default [%s] has no definition\n", csPrintable(def->style_name));
      }

      StyleData styleData = StyleData(def->style_reference, def->style_definition);
      rtf_Style.insert(def->style_name, styleData);
      def++;
   }

   // overwrite some (or all) definitions from file
   if (! rtfStyleSheetFile.isEmpty()) {
      loadStylesheet(rtfStyleSheetFile, rtf_Style);
   }

   // if user has defined an extension file, load its content.
   if (! rtfExtensionsFile.isEmpty()) {
      loadExtensions(rtfExtensionsFile);
   }

   createSubDirs(d);
}

static QString makeIndexName(const QString &s, int i)
{
   QString result = s;
   result += QString::number(i);

   return result;
}

void RTFGenerator::beginRTFDocument()
{
   static const QString paperName = Config::getEnum("rtf-paper-type");

   // all the included RTF files should begin with the same header

   m_textStream << "{\\rtf1\\ansi\\ansicpg" << theTranslator->trRTFansicp();
   m_textStream << "\\uc1 \\deff0\\deflang1033\\deflangfe1033\n";

   DBG_RTF(m_textStream << "{\\comment Beginning font list}\n")

   m_textStream << "{\\fonttbl ";
   m_textStream << "{\\f0\\froman\\fcharset" << theTranslator->trRTFCharSet();
   m_textStream << "\\fprq2{\\*\\panose 02020603050405020304}Times New Roman;}\n";
   m_textStream << "{\\f1\\fswiss\\fcharset" << theTranslator->trRTFCharSet();
   m_textStream << "\\fprq2{\\*\\panose 020b0604020202020204}Arial;}\n";
   m_textStream << "{\\f2\\fmodern\\fcharset" << theTranslator->trRTFCharSet();
   m_textStream << "\\fprq1{\\*\\panose 02070309020205020404}Courier New;}\n";
   m_textStream << "{\\f3\\froman\\fcharset2\\fprq2{\\*\\panose 05050102010706020507}Symbol;}\n";
   m_textStream << "}\n";

   DBG_RTF(m_textStream << "{\\comment begin colors}\n")

   m_textStream << "{\\colortbl;";
   m_textStream << "\\red0\\green0\\blue0;";
   m_textStream << "\\red0\\green0\\blue255;";
   m_textStream << "\\red0\\green255\\blue255;";
   m_textStream << "\\red0\\green255\\blue0;";
   m_textStream << "\\red255\\green0\\blue255;";
   m_textStream << "\\red255\\green0\\blue0;";
   m_textStream << "\\red255\\green255\\blue0;";
   m_textStream << "\\red255\\green255\\blue255;";
   m_textStream << "\\red0\\green0\\blue128;";
   m_textStream << "\\red0\\green128\\blue128;";
   m_textStream << "\\red0\\green128\\blue0;";
   m_textStream << "\\red128\\green0\\blue128;";
   m_textStream << "\\red128\\green0\\blue0;";
   m_textStream << "\\red128\\green128\\blue0;";
   m_textStream << "\\red128\\green128\\blue128;";
   m_textStream << "\\red192\\green192\\blue192;";

   // code highlighting colors. Note order is important see also RTFGenerator::startFontClass
   m_textStream << "\\red0\\green128\\blue0;";   // keyword = index 17
   m_textStream << "\\red96\\green64\\blue32;";  // keywordtype
   m_textStream << "\\rede0\\green128\\blue0;";  // keywordflow
   m_textStream << "\\red128\\green0\\blue0;";   // comment
   m_textStream << "\\red128\\green96\\blue32;"; // preprocessor
   m_textStream << "\\red0\\green32\\blue128;";  // stringliteral
   m_textStream << "\\red0\\green128\\blue128;"; // charliteral

   m_textStream << "}\n";
   DBG_RTF(m_textStream << "{\\comment Beginning style list}\n")

   m_textStream << "{\\stylesheet\n";
   m_textStream << "{\\widctlpar\\adjustright \\fs20\\cgrid \\snext0 Normal;}\n";

   // set the paper dimensions according to the rtf paper type
   m_textStream << "{";

   if (paperName == "a4") {
      // width & height values are inches * 1440
      m_textStream << "\\paperw11900\\paperh16840";

   } else if (paperName == "letter") {
      m_textStream << "\\paperw12240\\paperh15840";

   } else if (paperName == "legal") {
      m_textStream << "\\paperw12240\\paperh20160";

   } else if (paperName == "executive") {
      m_textStream << "\\paperw10440\\paperh15120";
   }

   m_textStream << "\\margl1800\\margr1800\\margt1440\\margb1440\\gutter0\\ltrsect}\n";

   // sort styles ascending by \s-number via an intermediate QArray
   QVector<const StyleData *> array(128);
   array.fill(nullptr);

   for (auto iter = rtf_Style.begin(); iter != rtf_Style.end(); ++iter)  {
      unsigned index = iter->m_index;
      unsigned size  = array.size();

      if (index >= size) {
         // +1 to add at least one element, then align up to multiple of 8
         array.resize((index + 1 + 7) & ~7);
         array.fill(nullptr, size);
         assert(index < array.size());
      }

      if (array.at(index) != nullptr) {
         QString key(iter.key());
         msg("Style '%s' redefines \\s%d.\n", csPrintable(key), index);
      }

      array[index] = &iter.value();
   }

   // write array elements
   unsigned size = array.size();

   for (unsigned i = 0; i < size; i++) {
      const StyleData *style = array.at(i);

      if (style != nullptr) {
         m_textStream << "{" << style->m_reference << style->m_definition << ";}\n";
      }
   }

   m_textStream << "}" << endl;

   // this comment is needed for postprocessing
   m_textStream << "{\\comment begin body}" << endl;

}

void RTFGenerator::beginRTFChapter()
{
   static const bool rtfCompact = Config::getBool("rtf-compact");

   DBG_RTF(m_textStream << "{\\comment BeginRTFChapter}\n")

   m_textStream << "\n";
   m_textStream << rtf_Style_Reset;

   if (rtfCompact) {
      // no extra page breaks
      m_textStream << "\\sect\\sbknone\n";
      rtfwriteRuler_thick();

   } else {
      m_textStream << "\\sect\\sbkpage\n";
   }

   m_textStream << rtf_Style["Heading1"].m_reference << "\n";
}

void RTFGenerator::beginRTFSection()
{
   static const bool rtfCompact = Config::getBool("rtf-compact");

   DBG_RTF(m_textStream << "{\\comment BeginRTFSection}\n")

   m_textStream << "\n";
   m_textStream << rtf_Style_Reset;

   if (rtfCompact) {
      // no extra page breaks
      m_textStream << "\\sect\\sbknone\n";
      rtfwriteRuler_emboss();

   } else {
      m_textStream << "\\sect\\sbkpage\n";

   }

   m_textStream << rtf_Style["Heading2"].m_reference << "\n";
}

void RTFGenerator::startFile(const QString &name, const QString &, const QString &)
{
   QString fileName = name;
   relPath = relativePathToRoot(fileName);

   if (! fileName.endsWith(".rtf")) {
      fileName += ".rtf";
   }

   startPlainFile(fileName);
   beginRTFDocument();
}

void RTFGenerator::endFile()
{
   DBG_RTF(m_textStream << "{\\comment endFile}\n")

   m_textStream << "}";
   endPlainFile();
}

void RTFGenerator::startProjectNumber()
{
   DBG_RTF(m_textStream << "{\\comment startProjectNumber }" << endl)
   m_textStream << " ";
}

void RTFGenerator::endProjectNumber()
{
   DBG_RTF(t << "{\\comment endProjectNumber }" << endl)
}

void RTFGenerator::startIndexSection(IndexSections is)
{
   m_listLevel = 0;

   switch (is) {
      case isTitlePageStart:
         // basic RTFstart

         m_textStream << "{\\info \n";
         m_textStream << "{\\title {\\comment ";
         break;

      case isTitlePageAuthor:
         m_textStream << "}\n";

         if (! rtf_subject.isEmpty()) {
            m_textStream << "{\\subject "  << rtf_subject      << "}\n";
         }

         if (! rtf_comments.isEmpty()) {
            m_textStream << "{\\comment "  << rtf_comments     << "}\n";
         }

         if (! rtf_company.isEmpty()) {
            m_textStream << "{\\company "  << rtf_company      << "}\n";
         }

         if (! rtf_author.isEmpty()) {
            m_textStream << "{\\author "   << rtf_author       << "}\n";
         }

         if (! rtf_manager.isEmpty()) {
            m_textStream << "{\\manager "  << rtf_manager      << "}\n";
         }

         if (! rtf_documentType.isEmpty()) {
            m_textStream << "{\\category " << rtf_documentType << "}\n";
         }

         if (! rtf_keywords.isEmpty()) {
            m_textStream << "{\\keywords " << rtf_keywords     << "}\n";
         }

         m_textStream << "{\\comment ";
         break;

      case isMainPage:
         //Introduction
         beginRTFChapter();
         break;

      //case isPackageIndex:
      //  //Package Index
      //  beginRTFChapter();
      //  break;

      case isModuleIndex:
         //Module Index
         beginRTFChapter();
         break;

      case isDirIndex:
         //Directory Index
         beginRTFChapter();
         break;

      case isNamespaceIndex:
         //Namespace Index
         beginRTFChapter();
         break;

      case isConceptIndex:
         //Concept Index
         beginRTFChapter();
         break;

      case isClassHierarchyIndex:
         //Hierarchical Index
         DBG_RTF(m_textStream << "{\\comment start classhierarchy}\n")
         beginRTFChapter();
         break;

      case isCompoundIndex:
         //Annotated Compound Index
         beginRTFChapter();
         break;

      case isFileIndex:
         //Annotated File Index
         beginRTFChapter();
         break;

      case isPageIndex:
         //Related Page Index
         beginRTFChapter();
         break;

      case isModuleDocumentation:
       {
         // Module Documentation
         bool found = false;

         for (auto gd : Doxy_Globals::groupSDict)  {

            if (found) {
               break;
            }

            if (! gd->isReference()) {
               beginRTFChapter();
               found = true;
            }
         }
      }
      break;

      case isDirDocumentation:   {
         // Directory Documentation
         bool found = false;

         for (auto dd : Doxy_Globals::directories)  {

            if (found) {
               break;
            }

            if (dd->isLinkableInProject()) {
               beginRTFChapter();
               found = true;
            }
         }
      }
      break;

      case isNamespaceDocumentation: {
         // Namespace Documentation
         bool found = false;

         for (auto &nd : Doxy_Globals::namespaceSDict)  {

            if (found) {
               break;
            }

            if (nd->isLinkableInProject()) {
               beginRTFChapter();
               found = true;
            }
         }
      }
      break;

      case isConceptDocumentation: {
         // Concept Documentation
         for (const auto &conceptDef : Doxy_Globals::conceptSDict) {
            if (conceptDef->isLinkableInProject()) {
               beginRTFChapter();
               break;
            }
         }
      }
      break;

      case isClassDocumentation: {
         // Compound Documentation
         bool found = false;

         for (auto cd : Doxy_Globals::classSDict)  {

            if (found) {
               break;
            }

            if (cd->isLinkableInProject() && cd->templateMaster() == nullptr && ! cd->isEmbeddedInOuterScope()) {
               beginRTFChapter();
               found = true;
            }
         }
      }

      break;

      case isFileDocumentation: {
         // File Documentation
         bool isFirst = true;

         for (auto &fn : Doxy_Globals::inputNameList)  {

            for (auto fd : *fn)  {

               if (fd->isLinkableInProject()) {
                  if (isFirst) {
                     beginRTFChapter();
                     isFirst = false;
                     break;
                  }
               }
            }
         }
      }
      break;

      case isExampleDocumentation: {
         // Example Documentation
         beginRTFChapter();
      }
      break;

      case isPageDocumentation: {
         // Page Documentation
         beginRTFChapter();
      }
      break;

      case isPageDocumentation2: {
         m_textStream  << "{\\tc \\v ";
      }
      break;

      case isEndIndex:
         break;
   }
}

void RTFGenerator::endIndexSection(IndexSections indexSec)
{
   static const bool fortranOpt        = Config::getBool("optimize-fortran");
   static const bool sourceCode        = Config::getBool("source-code");
   static const QString projectName    = Config::getString("project-name");
   static const QString projectVersion = Config::getString("project-version");

   switch (indexSec) {
      case isTitlePageStart:

         if (! rtf_title.isEmpty()) {
            // User has overridden document title in extensions file
            m_textStream << "}" << rtf_title;
         } else {
            m_textStream << "}" << projectName;
         }

         break;

      case isTitlePageAuthor: {
         m_textStream << " DoxyPress. }\n";
         m_textStream << "{\\creatim " << dateToRTFDateString() << "}\n}";

         DBG_RTF(m_textStream << "{\\comment end of infoblock}\n");

         // setup for this section
         m_textStream << rtf_Style_Reset << "\n";
         m_textStream << "\\sectd\\pgnlcrm\n";
         m_textStream << "{\\footer " << rtf_Style["Footer"].m_reference << "{\\chpgn}}\n";

         // the title entry
         DBG_RTF(t << "{\\comment begin title page}\n")

         m_textStream  << rtf_Style_Reset << rtf_Style["SubTitle"].m_reference << endl; // set to title style

         m_textStream  << "\\vertalc\\qc\\par\\par\\par\\par\\par\\par\\par\n";
         if (! rtf_logoFilename.isEmpty()) {
            m_textStream  << "{\\field\\flddirty {\\*\\fldinst INCLUDEPICTURE \"" << rtf_logoFilename;
            m_textStream  << "\" \\\\d \\\\*MERGEFORMAT} {\\fldrslt IMAGE }}\\par\\par\n";
         }

         if (! rtf_company.isEmpty()) {
            m_textStream  << rtf_company << "\\par\\par\n";
         }

         m_textStream  << rtf_Style_Reset << rtf_Style["Title"].m_reference << endl; // set to title style

         if (! rtf_title.isEmpty()) {
            // User has overridden document title in extensions file
            m_textStream << "{\\field\\fldedit {\\*\\fldinst TITLE \\\\*MERGEFORMAT}{\\fldrslt "
                         << rtf_title << "}}\\par" << endl;

         } else {
            DocText *root = validatingParseText(projectName);
            m_textStream << "{\\field\\fldedit {\\*\\fldinst TITLE \\\\*MERGEFORMAT}{\\fldrslt ";

            writeDoc(root, QSharedPointer<Definition>(), QSharedPointer<MemberDef>());
            m_textStream << "}}\\par" << endl;
         }

         m_textStream  << rtf_Style_Reset << rtf_Style["SubTitle"].m_reference << endl; // set to title style
         m_textStream  << "\\par\n";

         if (! rtf_documentType.isEmpty()) {
            m_textStream  << rtf_documentType << "\\par\n";
         }

         if (! rtf_documentId.isEmpty()) {
            m_textStream  << rtf_documentId << "\\par\n";
         }

         m_textStream  << "\\par\\par\\par\\par\\par\\par\\par\\par\\par\\par\\par\\par\n";

         m_textStream  << rtf_Style_Reset << rtf_Style["SubTitle"].m_reference << endl; // set to subtitle style

         if (! rtf_author.isEmpty())  {
           m_textStream << "{\\field\\fldedit {\\*\\fldinst AUTHOR \\\\*MERGEFORMAT}{\\fldrslt "<< rtf_author << " }}\\par" << endl;
         } else  {
           m_textStream << "{\\field\\fldedit {\\*\\fldinst AUTHOR \\\\*MERGEFORMAT}{\\fldrslt AUTHOR}}\\par" << endl;
         }

         m_textStream << theTranslator->trVersion() << " " << projectVersion << "\\par";

         m_textStream  << "{\\field\\fldedit {\\*\\fldinst CREATEDATE \\\\*MERGEFORMAT}"
                  "{\\fldrslt "<< dateToString(false) << " }}\\par"<<endl;
         m_textStream  << "\\page\\page";

         DBG_RTF(m_textStream  << "{\\comment End title page}" << endl)

         // table of contents section
         DBG_RTF(t << "{\\comment Table of contents}\n")
         m_textStream  << "\\vertalt\n";
         m_textStream  << rtf_Style_Reset << endl;
         m_textStream  << rtf_Style["Heading1"].m_reference;
         m_textStream  << theTranslator->trRTFTableOfContents() << "\\par" << endl;
         m_textStream  << rtf_Style_Reset << "\\par" << endl;
         m_textStream  << "{\\field\\fldedit {\\*\\fldinst TOC \\\\f \\\\*MERGEFORMAT}{\\fldrslt Table of contents}}\\par\n";
         m_textStream  << rtf_Style_Reset << endl;
      }
      break;

      case isMainPage:
         m_textStream  << "\\par " << rtf_Style_Reset << endl;

         if (! Doxy_Globals::mainPage || Doxy_Globals::mainPage->title().isEmpty()) {
            m_textStream  << "{\\tc \\v " << theTranslator->trMainPage() << "}" << endl;
         } else {
            m_textStream  << "{\\tc \\v " << substitute(Doxy_Globals::mainPage->title(), "%", "") << "}" << endl;
         }

         m_textStream  << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
         m_textStream  << "index";
         m_textStream  << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
         break;

      //case isPackageIndex:
      //  t << "\\par " << rtf_Style_Reset << endl;
      //  t << "{\\tc \\v " << theTranslator->trPackageList() << "}"<< endl;
      //  t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"packages.rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
      //  break;

      case isModuleIndex:
         m_textStream  << "\\par " << rtf_Style_Reset << endl;
         m_textStream  << "{\\tc \\v " << theTranslator->trModuleIndex() << "}" << endl;
         m_textStream  << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"modules.rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
         break;

      case isDirIndex:
         m_textStream  << "\\par " << rtf_Style_Reset << endl;
         m_textStream  << "{\\tc \\v " << theTranslator->trDirIndex() << "}" << endl;
         m_textStream  << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"dirs.rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
         break;

      case isNamespaceIndex:
         m_textStream  << "\\par " << rtf_Style_Reset << endl;
         if (fortranOpt) {
            m_textStream  << "{\\tc \\v " << theTranslator->trModulesIndex() << "}" << endl;
         } else {
            m_textStream  << "{\\tc \\v " << theTranslator->trNamespaceIndex() << "}" << endl;
         }

         m_textStream  << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"namespaces.rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
         break;

      case isConceptIndex:
         m_textStream  << "\\par " << rtf_Style_Reset << "\n";
         m_textStream  << "{\\tc \\v " << theTranslator->trConceptList() << "}\n";
         m_textStream  << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"concepts.rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
         break;

      case isClassHierarchyIndex:
         m_textStream  << "\\par " << rtf_Style_Reset << endl;
         m_textStream  << "{\\tc \\v " << theTranslator->trHierarchicalIndex() << "}" << endl;
         m_textStream  << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"hierarchy.rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
         break;

      case isCompoundIndex:
         m_textStream  << "\\par " << rtf_Style_Reset << endl;
         if (fortranOpt) {
            m_textStream  << "{\\tc \\v " << theTranslator->trCompoundIndexFortran() << "}" << endl;

         } else {
            m_textStream  << "{\\tc \\v " << theTranslator->trCompoundIndex() << "}" << endl;
         }
         m_textStream  << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"annotated.rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
         break;

      case isFileIndex:
         m_textStream  << "\\par " << rtf_Style_Reset << endl;
         m_textStream  << "{\\tc \\v " << theTranslator->trFileIndex() << "}" << endl;
         m_textStream  << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"files.rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
         break;

      case isPageIndex:
         m_textStream  << "\\par " << rtf_Style_Reset << endl;
         m_textStream  << "{\\tc \\v " << theTranslator->trPageIndex() << "}" << endl;
         m_textStream  << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"pages.rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
         break;

      case isModuleDocumentation: {
         m_textStream  << "{\\tc \\v " << theTranslator->trModuleDocumentation() << "}" << endl;

         for (auto gd : Doxy_Globals::groupSDict) {
            if (! gd->isReference()) {
               m_textStream  << "\\par " << rtf_Style_Reset << endl;
               m_textStream  << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
               m_textStream  << gd->getOutputFileBase();
               m_textStream  << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
            }
         }
      }

      break;

      case isDirDocumentation:
       {
         m_textStream  << "{\\tc \\v " << theTranslator->trDirDocumentation() << "}" << endl;

         for (auto dd :Doxy_Globals::directories) {
            if (dd->isLinkableInProject()) {
               m_textStream  << "\\par " << rtf_Style_Reset << endl;
               m_textStream  << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
               m_textStream  << dd->getOutputFileBase();
               m_textStream  << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
            }
         }
      }
      break;

      case isNamespaceDocumentation: {
         bool found = false;

         auto iter = Doxy_Globals::namespaceSDict.begin();

         for (auto &nd :Doxy_Globals::namespaceSDict) {
            if (found) {
               break;
            }

            if (nd->isLinkableInProject()) {
               m_textStream  << "\\par " << rtf_Style_Reset << endl;
               m_textStream  << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
               m_textStream  << nd->getOutputFileBase();
               m_textStream  << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
               found = true;
            }

            ++iter;
         }

         while (iter != Doxy_Globals::namespaceSDict.end()) {

            if ((*iter)->isLinkableInProject()) {
               m_textStream  << "\\par " << rtf_Style_Reset << endl;
               beginRTFSection();

               m_textStream  << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
               m_textStream  << (*iter)->getOutputFileBase();
               m_textStream  << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
            }

            ++iter;
         }
      }
      break;

      case isConceptDocumentation: {
         bool first = true;

         for (const auto &concepetDef : Doxy_Globals::conceptSDict) {
            if (concepetDef->isLinkableInProject()) {
               m_textStream << "\\par " << rtf_Style_Reset << "\n";

               if (! first) {
                  beginRTFSection();
               }

               first = false;

               m_textStream << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
               m_textStream << concepetDef->getOutputFileBase();
               m_textStream << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
            }
         }
      }
      break;

      case isClassDocumentation: {

         if (fortranOpt) {
            m_textStream  << "{\\tc \\v " << theTranslator->trTypeDocumentation() << "}" << endl;
         } else {
            m_textStream  << "{\\tc \\v " << theTranslator->trClassDocumentation() << "}" << endl;
         }

         bool firstTime = true;

         for (auto cd : Doxy_Globals::classSDict) {

            if (cd->isLinkableInProject() && cd->templateMaster() == nullptr && ! cd->isEmbeddedInOuterScope() ) {
               m_textStream  << "\\par " << rtf_Style_Reset << endl;

               if (! firstTime) {
                  beginRTFSection();
               }

               m_textStream  << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
               m_textStream  << cd->getOutputFileBase();
               m_textStream  << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";

               if (firstTime) {
                  firstTime = false;
               }
            }
         }
      }
      break;

      case isFileDocumentation: {
         bool isFirst = true;

         m_textStream  << "{\\tc \\v " << theTranslator->trFileDocumentation() << "}" << endl;

         for (auto &fn :Doxy_Globals::inputNameList) {
            for (auto fd :*fn) {

               if (fd->isLinkableInProject()) {
                  if (isFirst) {
                     m_textStream  << "\\par " << rtf_Style_Reset << endl;
                     m_textStream  << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
                     m_textStream  << fd->getOutputFileBase();
                     m_textStream  << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";

                     if (sourceCode && m_prettyCode && fd->generateSourceFile() ) {
                        m_textStream << "\\par " << rtf_Style_Reset << endl;
                        m_textStream << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"" << fd->getSourceFileBase()
                           << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
                     }

                     isFirst = false;

                  } else {
                     m_textStream  << "\\par " << rtf_Style_Reset << endl;
                     beginRTFSection();
                     m_textStream  << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
                     m_textStream  << fd->getOutputFileBase();
                     m_textStream  << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";

                     if (sourceCode && m_prettyCode && fd->generateSourceFile()) {
                        m_textStream << "\\par " << rtf_Style_Reset << endl;
                        m_textStream << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"" << fd->getSourceFileBase()
                           << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
                     }
                  }
               }

            }
         }
      }
      break;

      case isExampleDocumentation: {
         m_textStream  << "{\\tc \\v " << theTranslator->trExampleDocumentation() << "}" << endl;

         auto iter1 = Doxy_Globals::exampleSDict.begin();

         if (iter1 != Doxy_Globals::exampleSDict.end()) {
            m_textStream  << "\\par " << rtf_Style_Reset << endl;

            m_textStream  << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
            m_textStream  << (*iter1)->getOutputFileBase();
            m_textStream  << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";

            ++iter1;

            for (auto iter2 = iter1; iter2 != Doxy_Globals::exampleSDict.end(); ++iter2) {

               m_textStream  << "\\par " << rtf_Style_Reset << endl;
               beginRTFSection();

               m_textStream  << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
               m_textStream  << (*iter2)->getOutputFileBase();
               m_textStream  << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
            }

         }
      }
      break;

      case isPageDocumentation: {
         //#error "fix me in the same way as the latex index..."
         //t << "{\\tc \\v " << theTranslator->trPageDocumentation() << "}"<< endl;
         //t << "}"<< endl;
         //PageSDict::iterator pdi(*Doxy_Globals::pageSDict);
         //PageDef *pd=pdi.toFirst();
         //bool first=true;
         //for (pdi.toFirst();(pd=pdi.current());++pdi)
         //{
         //  if (!pd->getGroupDef() && !pd->isReference())
         //  {
         //    if (first) t << "\\par " << rtf_Style_Reset << endl;
         //    t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
         //    t << pd->getOutputFileBase();
         //    t << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
         //    first=false;
         //  }
         //}
      }
      break;

      case isPageDocumentation2: {
         m_textStream  << "}";
         m_textStream  << "\\par " << rtf_Style_Reset << endl;
      }
      break;

      case isEndIndex:
         beginRTFChapter();
         m_textStream  << rtf_Style["Heading1"].m_reference;
         m_textStream  << theTranslator->trRTFGeneralIndex() << "\\par " << endl;
         m_textStream  << rtf_Style_Reset << endl;
         m_textStream  << "{\\tc \\v " << theTranslator->trRTFGeneralIndex() << "}" << endl;
         m_textStream  << "{\\field\\fldedit {\\*\\fldinst INDEX \\\\c2 \\\\*MERGEFORMAT}{\\fldrslt INDEX}}\n";

         break;
   }
}

void RTFGenerator::writePageLink(const QString &name, bool first)
{
   if (first) {
      m_textStream  << "\\par " << rtf_Style_Reset << endl;
   }

   m_textStream  << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
   m_textStream  << name;
   m_textStream  << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
}

void RTFGenerator::lastIndexPage()
{
   DBG_RTF(t << "{\\comment Beginning Body of RTF Document}\n")

   // end page and setup for rest of document
   m_textStream  << "\\sect \\sbkpage \\pgndec \\pgnrestart\n";
   m_textStream  << "\\sect \\sectd \\sbknone\n";

   // set new footer with arabic numbers
   m_textStream  << "{\\footer " << rtf_Style["Footer"].m_reference << "{\\chpgn}}\n";
}

void RTFGenerator::writeStyleInfo(int)
{
}

void RTFGenerator::lineBreak(const QString &)
{
   DBG_RTF(t << "{\\comment (lineBreak)}"    << endl)
   m_textStream  << "\\par" << endl;
   m_omitParagraph = true;
}

void RTFGenerator::writeString(const QString &text)
{
   m_textStream  << text;
}

void RTFGenerator::startIndexList()
{
   DBG_RTF(m_textStream  << "{\\comment (startIndexList)}" << endl)
   m_textStream  << "{" << endl;
   m_textStream  << "\\par" << endl;

   incrementIndentLevel();

   m_textStream  << rtf_Style_Reset << rtf_LCList_DepthStyle() << endl;
   m_omitParagraph = true;
}

void RTFGenerator::endIndexList()
{
   DBG_RTF(t << "{\\comment (endIndexList)}" << endl)
   if (!m_omitParagraph) {
      m_textStream  << "\\par";
      m_omitParagraph = true;
   }

   m_textStream  << "}";
   decrementIndentLevel();
}

/*! start bullet list */
void RTFGenerator::startItemList()
{
   newParagraph();
   DBG_RTF(m_textStream << "{\\comment (startItemList level=" << m_listLevel << ") }" << endl)
   m_textStream << "{";
   incrementIndentLevel();
   rtf_listItemInfo[m_listLevel].isEnum = false;
}

/*! end bullet list */
void RTFGenerator::endItemList()
{
   newParagraph();
   DBG_RTF(m_textStream << "{\\comment (endItemList level=" << m_listLevel << ")}" << endl)
   m_textStream << "}";
   decrementIndentLevel();
   m_omitParagraph = true;
}

///*! start enumeration list */
//void RTFGenerator::startEnumList()  // starts an enumeration list
//{
//  DBG_RTF(m_textStream << "{\\comment (startEnumList)}" << endl)
//  m_textStream << "{" << endl;
//  incrementIndentLevel();
//  rtf_listItemInfo[m_listLevel].isEnum = true;
//  rtf_listItemInfo[m_listLevel].number = 1;
//}
//
///*! end enumeration list */
//void RTFGenerator::endEnumList()
//{
//  newParagraph();
//  DBG_RTF(m_textStream << "{\\comment (endEnumList)}" << endl)
//  m_textStream << "}";
//  decrementIndentLevel();
//  m_omitParagraph = true;
//}

/*! write bullet or enum item */
void RTFGenerator::startItemListItem()
{
   DBG_RTF(m_textStream << "{\\comment (startItemListItem)}" << endl)
   newParagraph();
   m_textStream << rtf_Style_Reset;
   if (rtf_listItemInfo[m_listLevel].isEnum) {
      m_textStream << rtf_EList_DepthStyle() << endl;
      m_textStream << rtf_listItemInfo[m_listLevel].number << ".\\tab ";
      rtf_listItemInfo[m_listLevel].number++;
   } else {
      m_textStream << rtf_BList_DepthStyle() << endl;
   }
   m_omitParagraph = true;
}

void RTFGenerator::endItemListItem()
{
   DBG_RTF(m_textStream << "{\\comment (endItemListItem)}" << endl)
}

void RTFGenerator::startIndexItem(const QString &, const QString &)
{
   DBG_RTF(m_textStream << "{\\comment (startIndexItem)}" << endl)

   if (! m_omitParagraph) {
      m_textStream << "\\par" << endl;
      m_omitParagraph = true;
   }
}

void RTFGenerator::endIndexItem(const QString &ref, const QString &fn)
{
   DBG_RTF(m_textStream << "{\\comment (endIndexItem)}" << endl)

   if (ref.isEmpty() && ! fn.isEmpty()) {
      m_textStream << "\\tab ";
      writeRTFReference(fn);
      m_textStream << endl;

   } else {
      m_textStream << endl;
   }

   m_omitParagraph = true;
}

//void RTFGenerator::writeIndexFileItem(const char *,const char *text)
//{
//  m_textStream << "\\item\\contentsline{section}{";
//  docify(text);
//  m_textStream << "}{\\pageref{" << text << "}}" << endl;
//}

void RTFGenerator::startHtmlLink(const QString &url)
{
   static const bool rtfHyperlinks = Config::getBool("rtf-hyperlinks");

   if (rtfHyperlinks) {
      m_textStream << "{\\field {\\*\\fldinst { HYPERLINK \"";
      m_textStream << url;
      m_textStream << "\" }{}";
      m_textStream << "}{\\fldrslt {\\cs37\\ul\\cf2 ";

   } else {
      startTypewriter();
   }
}

void RTFGenerator::endHtmlLink()
{
   static const bool rtfHyperlinks = Config::getBool("rtf-hyperlinks");

   if (rtfHyperlinks) {
      m_textStream << "}}}" << endl;
   } else {
      endTypewriter();
   }
}

//void RTFGenerator::writeMailLink(const char *url)
//{
//  startTypewriter();
//  docify(url);
//  endTypewriter();
//}

void RTFGenerator::writeStartAnnoItem(const QString &, const QString &f, const QString &path, const QString &name)
{
   static const bool rtfHyperlinks = Config::getBool("rtf-hyperlinks");

   DBG_RTF(m_textStream << "{\\comment (writeStartAnnoItem)}" << endl)
   m_textStream << "{\\b ";

   if (! path.isEmpty()) {
      docify(path);
   }

   if (! f.isEmpty() && rtfHyperlinks) {
      m_textStream << "{\\field {\\*\\fldinst { HYPERLINK  \\\\l \"";
      m_textStream << rtfFormatBmkStr(f);
      m_textStream << "\" }{}";
      m_textStream << "}{\\fldrslt {\\cs37\\ul\\cf2 ";

      docify(name);

      m_textStream << "}}}" << endl;

   } else {
      docify(name);
   }

   m_textStream << "} ";
}

void RTFGenerator::writeEndAnnoItem(const QString &name)
{
   DBG_RTF(m_textStream << "{\\comment (writeEndAnnoItem)}" << endl)

   if (! name.isEmpty()) {
      m_textStream << "\\tab ";
      writeRTFReference(name);
      m_textStream << endl;
   } else {
      m_textStream << endl;
   }

   newParagraph();
}

void RTFGenerator::startIndexKey()
{
   DBG_RTF(m_textStream << "{\\comment (startIndexKey)}" << endl)
   m_textStream << "{\\b ";
}

void RTFGenerator::endIndexKey()
{
   DBG_RTF(m_textStream << "{\\comment (endIndexKey)}" << endl)
}

void RTFGenerator::startIndexValue(bool hasBrief)
{
   DBG_RTF(m_textStream << "{\\comment (startIndexValue)}" << endl)

   m_textStream << " ";
   if (hasBrief) {
      m_textStream << "(";
   }
}

void RTFGenerator::endIndexValue(const QString &name, bool hasBrief)
{
   DBG_RTF(m_textStream << "{\\comment (endIndexValue)}" << endl)

   if (hasBrief) {
      m_textStream << ")";
   }
   m_textStream << "} ";

   if (! name.isEmpty()) {
      m_textStream << "\\tab ";
      writeRTFReference(name);
      m_textStream << endl;
   } else {
      m_textStream << endl;
   }
   m_omitParagraph = false;
   newParagraph();
}

void RTFGenerator::startSubsection()
{
   //beginRTFSubSection();
   m_textStream << "\n";
   DBG_RTF(m_textStream << "{\\comment Begin SubSection}\n")
   m_textStream << rtf_Style_Reset;
   m_textStream << rtf_Style["Heading3"].m_reference << "\n";
}

void RTFGenerator::endSubsection()
{
   newParagraph();
   m_textStream << rtf_Style_Reset << endl;
}

void RTFGenerator::startSubsubsection()
{
   //beginRTFSubSubSection();
   m_textStream << "\n";
   DBG_RTF(m_textStream << "{\\comment Begin SubSubSection}\n")
   m_textStream << "{" << endl;
   m_textStream << rtf_Style_Reset << rtf_Style["Heading4"].m_reference << "\n";
}

void RTFGenerator::endSubsubsection()
{
   newParagraph();
   m_textStream << "}" << endl;
}

void RTFGenerator::startTextLink(const QString &f, const QString &anchor)
{
   static const bool rtfHyperlinks = Config::getBool("rtf-hyperlinks");

   if (rtfHyperlinks) {
      QString ref;

      if (! f.isEmpty()) {
         ref += f;
      }

      if (! anchor.isEmpty()) {
         ref += '_';
         ref += anchor;
      }

      m_textStream << "{\\field {\\*\\fldinst { HYPERLINK  \\\\l \"";
      m_textStream << rtfFormatBmkStr(ref);
      m_textStream << "\" }{}";
      m_textStream << "}{\\fldrslt {\\cs37\\ul\\cf2 ";
   }
}

void RTFGenerator::endTextLink()
{
   static const bool rtfHyperlinks = Config::getBool("rtf-hyperlinks");

   if (rtfHyperlinks) {
      m_textStream << "}}}" << endl;
   }
}

void RTFGenerator::writeObjectLink(const QString &ref, const QString &f, const QString &anchor, const QString &text)
{
   static const bool rtfHyperlinks = Config::getBool("rtf-hyperlinks");

   if (ref.isEmpty() && rtfHyperlinks) {
      QString refName;

      if (! f.isEmpty()) {
         refName += f;
      }
      if (! anchor.isEmpty()) {
         refName += '_';
         refName += anchor;
      }

      m_textStream << "{\\field {\\*\\fldinst { HYPERLINK  \\\\l \"";
      m_textStream << rtfFormatBmkStr(refName);
      m_textStream << "\" }{}";
      m_textStream << "}{\\fldrslt {\\cs37\\ul\\cf2 ";

      docify(text);

      m_textStream << "}}}" << endl;

   } else {
      startBold();
      docify(text);
      endBold();
   }
}

void RTFGenerator::startPageRef()
{
   m_textStream << " (";
   startEmphasis();
}

void RTFGenerator::endPageRef(const QString &clname, const QString &anchor)
{
   QString ref;

   if (! clname.isEmpty()) {
      ref += clname;
   }

   if (! anchor.isEmpty()) {
      ref += '_';
      ref += anchor;
   }

   writeRTFReference(ref);
   endEmphasis();
   m_textStream << ")";
}

void RTFGenerator::writeCodeLink(const QString &ref, const QString &f, const QString &anchor,
                                 const QString &name, const QString &)
{
   static const bool rtfHyperlinks = Config::getBool("rtf-hyperlinks");

   if (ref.isEmpty() && rtfHyperlinks) {
      QString refName;

      if (! f.isEmpty()) {
         refName += f;
      }

      if (! anchor.isEmpty()) {
         refName += '_';
         refName += anchor;
      }

      m_textStream << "{\\field {\\*\\fldinst { HYPERLINK  \\\\l \"";
      m_textStream << rtfFormatBmkStr(refName);
      m_textStream << "\" }{}";
      m_textStream << "}{\\fldrslt {\\cs37\\ul\\cf2 ";

      codify(name);

      m_textStream << "}}}" << endl;

   } else {
      codify(name);
   }
}

void RTFGenerator::startTitleHead(const QString &)
{
   DBG_RTF(m_textStream << "{\\comment startTitleHead}" << endl)

   //    beginRTFSection();
   m_textStream << rtf_Style_Reset << rtf_Style["Heading2"].m_reference << endl;
}

void RTFGenerator::endTitleHead(const QString &fileName, const QString &name)
{
   DBG_RTF(m_textStream << "{\\comment endTitleHead}" << endl)
   m_textStream << "\\par " << rtf_Style_Reset << endl;

   if (! name.isEmpty()) {
      // make table of contents entry
      m_textStream << "{\\tc\\tcl2 \\v ";
      docify(name);

      m_textStream << "}" << endl;

      // make an index entry
      addIndexItemName(name, "");
      writeAnchor(fileName, "");
   }
}

void RTFGenerator::startTitle()
{
   static const bool rtfCompact = Config::getBool("rtf-compact");

   DBG_RTF( << "{\\comment startTitle}" << endl)

   if (rtfCompact) {
      beginRTFSection();
   } else {
      beginRTFChapter();
   }
}

void RTFGenerator::startGroupHeader(int extraIndent)
{
   DBG_RTF(m_textStream << "{\\comment startGroupHeader}" << endl)

   //newParagraph();
   m_textStream << rtf_Style_Reset;

   if (extraIndent == 2) {
      m_textStream << rtf_Style["Heading5"].m_reference;

   } else if (extraIndent == 1) {
      m_textStream << rtf_Style["Heading4"].m_reference;

   } else { // extraIndent==0
      m_textStream << rtf_Style["Heading3"].m_reference;
   }

   m_textStream << endl;
}

void RTFGenerator::endGroupHeader(int)
{
   DBG_RTF(m_textStream << "{\\comment endGroupHeader}" << endl)

   m_textStream << "\\par" << endl;
   m_textStream << rtf_Style_Reset << endl;
}

void RTFGenerator::startMemberDoc(const QString &clName, const QString &memName, const QString &, const QString &,
                  int, int, bool showInline)
{
   DBG_RTF(m_textStream << "{\\comment startMemberDoc}" << endl)

   if (! memName.isEmpty() && ! memName.startsWith('@')) {
      addIndexItemName(memName, clName);
      addIndexItemName(clName, memName);
   }

   m_textStream << rtf_Style_Reset << rtf_Style[showInline ? "Heading5" : "Heading4"].m_reference;
   m_textStream << "{" << endl;

   startBold();

   m_textStream << endl;
}

void RTFGenerator::endMemberDoc(bool)
{
   DBG_RTF(m_textStream << "{\\comment endMemberDoc}" << endl)

   endBold();
   m_textStream << "}" << endl;
   newParagraph();
}

void RTFGenerator::startDoxyAnchor(const QString &, const QString &, const QString &, const QString &, const QString &)
{
   DBG_RTF(m_textStream << "{\\comment startDoxyAnchor}" << endl)
}

void RTFGenerator::endDoxyAnchor(const QString &fName, const QString &anchor)
{
   QString ref;
   ref += fName;

   if (! anchor.isEmpty()) {
      ref += '_';
      ref += anchor;
   }

   DBG_RTF(m_textStream << "{\\comment endDoxyAnchor}" << endl)
   m_textStream << "{\\bkmkstart ";
   m_textStream << rtfFormatBmkStr(ref);
   m_textStream << "}" << endl;
   m_textStream << "{\\bkmkend ";
   m_textStream << rtfFormatBmkStr(ref);
   m_textStream << "}" << endl;
}


//void RTFGenerator::writeLatexLabel(const char *clName,const char *anchor)
//{
//  writeDoxyAnchor(0,clName,anchor,0);
//}

void RTFGenerator::addIndexItemName(const QString &s1, const QString &s2)
{
   if (! s1.isEmpty()) {
      m_textStream << "{\\xe \\v ";
      docify(s1);

      if (! s2.isEmpty()) {
         m_textStream << "\\:";
         docify(s2);
      }

      m_textStream << "}" << endl;
   }
}

void RTFGenerator::startIndent()
{
   incrementIndentLevel();
   DBG_RTF(m_textStream << "{\\comment (startIndent) }" << endl)

   m_textStream << "{" << endl;
   m_textStream << rtf_Style_Reset << rtf_CList_DepthStyle() << endl;
}

void RTFGenerator::endIndent()
{
   m_textStream << "}" << endl;
   decrementIndentLevel();
}

void RTFGenerator::startDescription()
{
   DBG_RTF(m_textStream << "{\\comment (startDescription)}"    << endl)
   m_textStream << "{" << endl;
   m_textStream << rtf_Style_Reset << rtf_DList_DepthStyle();
}

void RTFGenerator::endDescription()
{
   DBG_RTF(m_textStream << "{\\comment (endDescription)}"    << endl)
   newParagraph();
   m_textStream << "}";
}

void RTFGenerator::startDescItem()
{
   newParagraph();
   DBG_RTF(m_textStream << "{\\comment (startDescItem)}"    << endl)
   m_textStream << "{\\b ";
}

void RTFGenerator::endDescItem()
{
   DBG_RTF(m_textStream<< "{\\comment (endDescItem)}"    << endl)
   m_textStream << "}" << endl;
   newParagraph();
}

void RTFGenerator::startMemberDescription(const QString &, const QString &, bool)
{
   DBG_RTF(m_textStream << "{\\comment (startMemberDescription)}"    << endl)
   m_textStream << "{" << endl;

   incrementIndentLevel();
   m_textStream << rtf_Style_Reset << rtf_CList_DepthStyle();
   startEmphasis();
}

void RTFGenerator::endMemberDescription()
{
   DBG_RTF(m_textStream << "{\\comment (endMemberDescription)}"    << endl)
   endEmphasis();

   decrementIndentLevel();

   m_textStream << "\\par";
   m_textStream << "}" << endl;

   m_omitParagraph = true;
}

void RTFGenerator::startDescList(SectionTypes)
{
   DBG_RTF(m_textStream << "{\\comment (startDescList)}"    << endl)
   m_textStream << "{"; // ends at endDescList
   m_textStream << "{"; // ends at endDescTitle

   startBold();
   newParagraph();
}

//void RTFGenerator::endDescTitle()
//{
//  DBG_RTF(t << "{\\comment (endDescTitle) }"    << endl)
//  endBold();
//  t << "}";
//  newParagraph();
//  incrementIndentLevel();
//  t << rtf_Style_Reset << rtf_DList_DepthStyle();
//}

void RTFGenerator::startDescForItem()
{
   DBG_RTF(m_textStream << "{\\comment (startDescForItem) }"    << endl)
}

void RTFGenerator::endDescForItem()
{
   DBG_RTF(m_textStream << "{\\comment (endDescForItem) }"    << endl)
}

//void RTFGenerator::endDescList()
//{
//  DBG_RTF(t << "{\\comment (endDescList)}"    << endl)
//  newParagraph();
//  decrementIndentLevel();
//  m_omitParagraph = true;
//  t << "}";
//}


void RTFGenerator::startSection(const QString &, const QString &title, SectionInfo::SectionType type)
{
   DBG_RTF(m_textStream << "{\\comment (startSection)}"    << endl)
   m_textStream << "{";
   m_textStream << rtf_Style_Reset;

   int num = 4;

   switch (type) {
      case SectionInfo::Page:
         num = 2;
         break;
      case SectionInfo::Section:
         num = 3;
         break;
      case SectionInfo::Subsection:
         num = 4;
         break;
      case SectionInfo::Subsubsection:
         num = 4;
         break;
      case SectionInfo::Paragraph:
         num = 4;
         break;
      default:
         assert(0);
         break;
   }

   QString heading;
   heading = QString("Heading%1").formatArg(num);

   // set style
   m_textStream << rtf_Style[heading].m_reference;

   // make table of contents entry
   m_textStream << "{\\tc\\tcl" << num << " \\v ";

   docify(title);
   m_textStream << "}" << endl;
}

void RTFGenerator::endSection(const QString &label, SectionInfo::SectionType)
{
   DBG_RTF(m_textStream << "{\\comment (endSection)}"    << endl)

   // make bookmark
   m_omitParagraph = false;
   newParagraph();

   writeAnchor("", label);
   m_textStream << "}";
}

void RTFGenerator::docify(const QString &text)
{
   for (auto c : text) {

      switch (c.unicode()) {
         case '{':
            m_textStream << "\\{";
            break;

         case '}':
            m_textStream << "\\}";
            break;

         case '\\':
            m_textStream << "\\\\";
            break;

         default: {
            // see if we can insert an hyphenation hint
            m_textStream << c;
         }
      }

      m_omitParagraph = false;
   }
}

void RTFGenerator::codify(const QString &str)
{
   // note that RTF does not have a "verbatim", so "\n" means
   // nothing... add a "newParagraph()";
   // static char spaces[]="        ";

   static const int tabSize = Config::getInt("tab-size");

   for (auto c : str ) {
      int spacesToNextTabStop;

      switch (c.unicode()) {
         case '\t':
            spacesToNextTabStop = tabSize - (m_col % tabSize);
            m_textStream << QString(spacesToNextTabStop, ' ');

            m_col += spacesToNextTabStop;
            break;

         case '\n':
            newParagraph();
            m_textStream << '\n';
            m_col = 0;
            break;

         case '{':
            m_textStream << "\\{";
            ++m_col;
            break;

         case '}':
            m_textStream << "\\}";
            ++m_col;
            break;

         case '\\':
            m_textStream << "\\\\";
            ++m_col;
            break;

         default:
            m_textStream << c;
            ++m_col;
            break;
      }
   }
}

void RTFGenerator::writeChar(char c)
{
   QString tmp = QChar(c);
   docify(tmp);
}

void RTFGenerator::startClassDiagram()
{
   DBG_RTF(m_textStream << "{\\comment startClassDiagram }" << endl)
}

void RTFGenerator::endClassDiagram(const ClassDiagram &d, const QString &fname, const QString &)
{
   newParagraph();

   // create a png file
   d.writeImage(m_textStream, m_outputDir, relPath, fname, false);

   // display the file
   m_textStream << "{" << endl;
   m_textStream << rtf_Style_Reset << endl;
   m_textStream << "\\par\\pard \\qc {\\field\\flddirty {\\*\\fldinst INCLUDEPICTURE \"";
   m_textStream << fname << ".png\"";
   m_textStream << " \\\\d \\\\*MERGEFORMAT}{\\fldrslt IMAGE}}\\par" << endl;
   m_textStream << "}" << endl;
}

//void RTFGenerator::writeFormula(const char *,const char *text)
//{
//  m_textStream << text;
//}

void RTFGenerator::startMemberItem(const QString &, int, const QString &, bool deprecated)
{
   (void) deprecated;

   DBG_RTF(m_textStream << "{\\comment startMemberItem }" << endl)
   m_textStream << rtf_Style_Reset << rtf_BList_DepthStyle() << endl;    // set style to appropriate depth
}

void RTFGenerator::endMemberItem()
{
   DBG_RTF(m_textStream << "{\\comment endMemberItem }" << endl)
   newParagraph();
}

void RTFGenerator::writeAnchor(const QString &fileName, const QString &name)
{
   QString anchor;

   if (! fileName.isEmpty()) {
      anchor += fileName;
   }

   if (! fileName.isEmpty() && ! name.isEmpty()) {
      anchor += '_';
   }

   if (! name.isEmpty()) {
      anchor += name;
   }

   DBG_RTF(m_textStream << "{\\comment writeAnchor (" << anchor << ")}" << endl)

   m_textStream << "{\\bkmkstart " << rtfFormatBmkStr(anchor) << "}" << endl;
   m_textStream << "{\\bkmkend " << rtfFormatBmkStr(anchor) << "}" << endl;
}

void RTFGenerator::writeRTFReference(const QString &label)
{
   m_textStream << "{\\field\\fldedit {\\*\\fldinst PAGEREF ";
   m_textStream << rtfFormatBmkStr(label);
   m_textStream << " \\\\*MERGEFORMAT}{\\fldrslt pagenum}}";
}

void RTFGenerator::startCodeFragment(const QString &)
{
   m_textStream << "{" << endl;

   m_textStream << rtf_Style_Reset << rtf_Code_DepthStyle();

}

void RTFGenerator::endCodeFragment(const QString &)
{
   endCodeLine();

   m_textStream << "}" << endl;
   m_omitParagraph = true;
}

void RTFGenerator::writeNonBreakableSpace(int)
{
   m_textStream << "\\~ ";
}

void RTFGenerator::startMemberList()
{
   m_textStream << endl;
   DBG_RTF(m_textStream << "{\\comment (startMemberList) }"    << endl)
   m_textStream << "{" << endl;

#ifdef DELETEDCODE
   if (!insideTabbing) {
      m_textStream << "\\begin{CompactItemize}" << endl;
   }
#endif
}

void RTFGenerator::endMemberList()
{
   DBG_RTF(m_textStream << "{\\comment (endMemberList) }"    << endl)
   m_textStream << "}" << endl;

#ifdef DELETEDCODE
   if (!insideTabbing) {
      m_textStream << "\\end{CompactItemize}"   << endl;
   }
#endif
}

//void RTFGenerator::startImage(const char *name,const char *,bool)
//{
//  newParagraph();
//  m_textStream << "{" << endl;
//  m_textStream << rtf_Style_Reset << endl;
//  m_textStream << "\\par\\pard \\qc {\\field\\flddirty {\\*\\fldinst INCLUDEPICTURE ";
//  m_textStream << name;
//  m_textStream << " \\\\d \\\\*MERGEFORMAT}{\\fldrslt IMAGE}}\\par" << endl;
//  m_textStream << "}" << endl;
//}

//void RTFGenerator::endImage(bool)
//{
//  // not yet implemented
//}

//void RTFGenerator::startDotFile(const char *name,bool)
//{
//  QString baseName = name;
//  int i;
//  if ((i=baseName.lastIndexOf('/'))!=-1 || (i=baseName.lastIndexOf('\\'))!=-1)
//  {
//    baseName=baseName.right(baseName.length()-i-1);
//  }

//  writeDotGraphFromFile(name, m_outputDir,baseName,BITMAP);
//  newParagraph();
//
//  m_textStream << "{" << endl;
//  m_textStream << rtf_Style_Reset << endl;
//  m_textStream << "\\par\\pard \\qc {\\field\\flddirty {\\*\\fldinst INCLUDEPICTURE ";
//  m_textStream << outDir << "\\" << baseName;
//  m_textStream << " \\\\d \\\\*MERGEFORMAT}{\\fldrslt IMAGE}}\\par" << endl;
//  m_textStream << "}" << endl;
//}

//void RTFGenerator::endDotFile(bool)
//{
//  // not yet implemented
//}
//

void RTFGenerator::startEnumTable()
{
   startSimpleSect(EnumValues, QString(), QString(), theTranslator->trEnumerationValues());
   startDescForItem();
}

void RTFGenerator::endEnumTable()
{
   DBG_RTF(m_textStream << "{\\comment (endDescTable)}"      << endl)

   endDescForItem();
   endSimpleSect();
}

void RTFGenerator::startDescTable(const QString &title)
 {
   DBG_RTF(t << "{\\comment (startDescTable) }"    << endl)

   m_textStream << "{\\par" << endl;
   m_textStream << "{" << rtf_Style["Heading5"].m_reference << endl;

   docify(title);

   m_textStream << ":\\par}" << endl;
   m_textStream << rtf_Style_Reset << rtf_DList_DepthStyle();

   m_textStream << "\\trowd \\trgaph108\\trleft426\\tblind426"
       "\\trbrdrt\\brdrs\\brdrw10\\brdrcf15 "
       "\\trbrdrl\\brdrs\\brdrw10\\brdrcf15 "
       "\\trbrdrb\\brdrs\\brdrw10\\brdrcf15 "
       "\\trbrdrr\\brdrs\\brdrw10\\brdrcf15 "
       "\\trbrdrh\\brdrs\\brdrw10\\brdrcf15 "
       "\\trbrdrv\\brdrs\\brdrw10\\brdrcf15 "<< endl;

   int i;
   int columnPos[2] = { 25, 100 };

   for (i = 0; i < 2; i++) {
      m_textStream << "\\clvertalt\\clbrdrt\\brdrs\\brdrw10\\brdrcf15 "
           "\\clbrdrl\\brdrs\\brdrw10\\brdrcf15 "
           "\\clbrdrb\\brdrs\\brdrw10\\brdrcf15 "
           "\\clbrdrr \\brdrs\\brdrw10\\brdrcf15 "
           "\\cltxlrtb "
           "\\cellx" << (rtf_pageWidth*columnPos[i]/100) << endl;
   }

   m_textStream << "\\pard \\widctlpar\\intbl\\adjustright" << endl;
 }

void RTFGenerator::endDescTable()
{
   DBG_RTF(t << "{\\comment (endDescTable)}"      << endl)
   m_textStream << "}" << endl;
}

void RTFGenerator::startDescTableRow()
{
}

void RTFGenerator::endDescTableRow()
{
}

void RTFGenerator::startDescTableTitle()
{
   DBG_RTF(m_textStream << "{\\comment (startDescTableTitle) }"    << endl)
   m_textStream << "{\\qr ";
}

void RTFGenerator::endDescTableTitle()
{
   DBG_RTF(m_textStream << "{\\comment (endDescTableTitle) }"    << endl)
   m_textStream << "\\cell }";
}

void RTFGenerator::startDescTableData()
{
   DBG_RTF(m_textStream << "{\\comment (startDescTableData) }"    << endl)
   m_textStream << "{";
}

void RTFGenerator::endDescTableData()
{
   DBG_RTF(m_textStream << "{\\comment (endDescTableData) }"    << endl)
   m_textStream << "\\cell }{\\row }" << endl;
}

// a style for list formatted as a "bulleted list"

void RTFGenerator::incrementIndentLevel()
{
   m_listLevel++;
   if (m_listLevel > rtf_maxIndentLevels - 1) {
      err("Maximum indent level (%d) exceeded while generating RTF output\n", rtf_maxIndentLevels);
      m_listLevel = rtf_maxIndentLevels - 1;
   }
}

void RTFGenerator::decrementIndentLevel()
{
   m_listLevel--;

   if (m_listLevel < 0) {
      err("Negative indent level while generating RTF output\n");
      m_listLevel = 0;
   }
}

// a style for list formatted with "list continue" style
QString RTFGenerator::rtf_CList_DepthStyle()
{
   QString n = makeIndexName("ListContinue", m_listLevel);
   return rtf_Style[n].m_reference;
}

// a style for list formatted as a "latext style" table of contents
QString RTFGenerator::rtf_LCList_DepthStyle()
{
   QString n = makeIndexName("LatexTOC", m_listLevel);
   return rtf_Style[n].m_reference;
}

// a style for list formatted as a "bullet" style
QString RTFGenerator::rtf_BList_DepthStyle()
{
   QString n = makeIndexName("ListBullet", m_listLevel);
   return rtf_Style[n].m_reference;
}

// a style for list formatted as a "enumeration" style
QString RTFGenerator::rtf_EList_DepthStyle()
{
   QString n = makeIndexName("ListEnum", m_listLevel);
   return rtf_Style[n].m_reference;
}

QString RTFGenerator::rtf_DList_DepthStyle()
{
   QString n = makeIndexName("DescContinue", m_listLevel);
   return rtf_Style[n].m_reference;
}

QString RTFGenerator::rtf_Code_DepthStyle()
{
   QString n = makeIndexName("CodeExample", m_listLevel);
   return rtf_Style[n].m_reference;
}

void RTFGenerator::startTextBlock(bool dense)
{
   DBG_RTF(m_textStream << "{\\comment startTextBlock}" << endl)

   m_textStream << "{" << endl;
   m_textStream << rtf_Style_Reset;

   if (dense) {   // no spacing between "paragraphs"
      m_textStream << rtf_Style["DenseText"].m_reference;

   } else {       // some spacing
      m_textStream << rtf_Style["BodyText"].m_reference;
   }
}

void RTFGenerator::endTextBlock(bool /*paraBreak*/)
{
   newParagraph();
   DBG_RTF(m_textStream << "{\\comment endTextBlock}" << endl)
   m_textStream << "}" << endl;
   //m_omitParagraph = true;
}

void RTFGenerator::newParagraph()
{
   if (!m_omitParagraph) {
      DBG_RTF(m_textStream << "{\\comment (newParagraph)}"    << endl)
      m_textStream << "\\par" << endl;
   }
   m_omitParagraph = false;
}

void RTFGenerator::startParagraph(const QString &className)
{
   DBG_RTF(m_textStream << "{\\comment startParagraph}" << endl)
   newParagraph();
   m_textStream << "{" << endl;

   if (className == "reference") {
      m_textStream << "\\ql" << endl;
   }
}

void RTFGenerator::endParagraph()
{
   DBG_RTF(m_textStream << "{\\comment endParagraph}" << endl)
   m_textStream << "}\\par" << endl;
   m_omitParagraph = true;
}

void RTFGenerator::startMemberSubtitle()
{
   DBG_RTF(m_textStream << "{\\comment startMemberSubtitle}" << endl)
   m_textStream << "{" << endl;
   m_textStream << rtf_Style_Reset << rtf_CList_DepthStyle() << endl;
}

void RTFGenerator::endMemberSubtitle()
{
   DBG_RTF(m_textStream << "{\\comment endMemberSubtitle}" << endl)
   newParagraph();
   m_textStream << "}" << endl;
}

bool isLeadBytes(int c)
{
   bool result;

   QString codePage = theTranslator->trRTFansicp();

   if (codePage == "932") {     // cp932 (Japanese Shift-JIS)
      result = (0x81 <= c && c <= 0x9f) || (0xe0 <= c && c <= 0xfc);

   } else if (codePage == "936") { // cp936 (Simplified Chinese GBK)
      result = 0x81 <= c && c <= 0xFE;

   } else if (codePage == "949") { // cp949 (Korean)
      result = 0x81 <= c && c <= 0xFE;

   } else if (codePage == "950") { // cp950 (Traditional Chinese Big5)
      result = 0x81 <= c && c <= 0xFE;

   } else {                     // for SBCS Codepages (cp1252,1251 etc...)
      result = false;

   }

   return result;
}


// note: function is not reentrant!
static void encodeForOutput(QTextStream &t_stream, const QString &text)
{
   if (text.isEmpty()) {
      return;
   }

   static QByteArray enc;
   QString outputEncoding = QString("CP%1").formatArg(theTranslator->trRTFansicp());

   QTextCodec *outCodec = QTextCodec::codecForName(outputEncoding.toUtf8());

   if (! outCodec) {
      err("Unsupported character conversion: '%s': %s\n", csPrintable(outputEncoding), strerror(errno));
      Doxy_Work::stopDoxyPress();
   }

   QString temp = text;
   enc = outCodec->fromUnicode(temp);

   bool multiByte = false;

   for (int i = 0; i < enc.size(); i++) {
      uchar c = (uchar)enc.at(i);

      if (c >= 0x80 || multiByte) {
         char esc[10];
         sprintf(esc, "\\'%X", c);       // escape sequence for SBCS and DBCS(1st&2nd bytes).
         t_stream << esc;

         if (! multiByte) {
            multiByte = isLeadBytes(c);   // it may be DBCS Codepages.

         } else {
            multiByte = false;            // end of Double Bytes Character.
         }

      } else {
         t_stream << (char)c;
      }
   }
}

/**
 * modify carefully, recursive code
 */
static bool preProcessFile_RTF(QString &input_FName, QTextStream &t_stream, bool bIncludeHeader = true)
{
   static const bool showFiles = Config::getBool("show-file-page");

   QFile f(input_FName);

   if (! f.open(QIODevice::ReadOnly)) {

      if (! f.exists())  {

         if (input_FName == "files.rtf" && ! showFiles)  {
            return true;

         } else {
            err("Unable to locate missing file '%s', contact the developers\n", csPrintable(input_FName));
         }

      } else {
         err("Unable to open file %s for reading, OS Error #: %d  \n", csPrintable(input_FName), f.error());
      }

      return false;
   }

   QByteArray lineBuf;

   // scan until find end of header, this works because the first line of the rtf file
   // before the body, ALWAYS contains "{\comment begin body}"

   while (true) {
      // retrieve header from refman.rtf and copy to combined.rtf
      // skip over the header for all other files

      lineBuf = f.readLine();

      if (f.error() != QFile::NoError) {
         err("Unable to open file %s for reading, OS Error #: %d\n", csPrintable(input_FName), f.error());
         return false;
      }

      if (lineBuf.contains("\\comment begin body")) {
         break;
      }

      if (bIncludeHeader) {
         encodeForOutput(t_stream, QString::fromUtf8(lineBuf));
      }
   }

   while (true) {
      lineBuf = f.readLine();

      if (f.error() != QFile::NoError) {
         err("Unable to open file %s for reading, OS Error #: %d\n", csPrintable(input_FName), f.error());
         return false;
      }

      int pos = lineBuf.indexOf("INCLUDETEXT");

      if (pos != -1) {
         int startNamePos = lineBuf.indexOf('"', pos) + 1;
         int endNamePos   = lineBuf.indexOf('"', startNamePos);

         QString fileName = lineBuf.mid(startNamePos, endNamePos - startNamePos);

         DBG_RTF(t_stream << "{\\comment begin include " << fileName << "}" << endl)

         if (! preProcessFile_RTF(fileName, t_stream, false)) {
            return false;
         }

         DBG_RTF(t_stream << "{\\comment end include " << fileName << "}" << endl)

      } else {
         // no INCLUDETEXT on this line,
         // odd code to skip  the final "}" if we did not include the headers

         if (! f.atEnd() || bIncludeHeader) {
            encodeForOutput(t_stream, QString::fromUtf8(lineBuf));

         } else {
            // last line of included file has a "}" which needs to be removed

            int bracePos = lineBuf.lastIndexOf('}');

            if (bracePos != -1) {
               // truncate the last char, odd but it solves the problem
               lineBuf.truncate(bracePos);

            } else {
               err("Last character of %s was not a '}' as expected.\n", csPrintable(input_FName) );

            }

            encodeForOutput(t_stream, QString::fromUtf8(lineBuf));
         }
      }

      if (f.atEnd()) {
         break;
      }
   }

   f.close();

   // remove temporary file
   QFile::remove(input_FName);

   return true;
}

void RTFGenerator::startDotGraph()
{
   DBG_RTF(m_textStream << "{\\comment (startDotGraph)}"    << endl)
}

void RTFGenerator::endDotGraph(const DotClassGraph &g)
{
   static const QString imageExt  = Config::getEnum("dot-image-extension");

   newParagraph();

   QString fn = g.writeGraph(m_textStream , GOF_BITMAP, EOF_Rtf, m_outputDir, m_fileName, relPath, true, false);

   // display the file
   m_textStream << "{" << endl;
   m_textStream << rtf_Style_Reset << endl;
   m_textStream << "\\par\\pard \\qc {\\field\\flddirty {\\*\\fldinst INCLUDEPICTURE \"";
   m_textStream << fn << "." << imageExt;
   m_textStream << "\" \\\\d \\\\*MERGEFORMAT}{\\fldrslt IMAGE}}\\par" << endl;
   m_textStream << "}" << endl;
   newParagraph();

   DBG_RTF(m_textStream << "{\\comment (endDotGraph)}"    << endl)
}

void RTFGenerator::startInclDepGraph()
{
   DBG_RTF(m_textStream << "{\\comment (startInclDepGraph)}"    << endl)
}

void RTFGenerator::endInclDepGraph(const DotInclDepGraph &g)
{
   static const QString imageExt  = Config::getEnum("dot-image-extension");

   newParagraph();

   QString fn = g.writeGraph(m_textStream, GOF_BITMAP, EOF_Rtf, m_outputDir, m_fileName, relPath, false);

   // display the file
   m_textStream << "{" << endl;
   m_textStream << rtf_Style_Reset << endl;
   m_textStream << "\\par\\pard \\qc {\\field\\flddirty {\\*\\fldinst INCLUDEPICTURE \"";
   m_textStream << fn << "." << imageExt;
   m_textStream << "\" \\\\d \\\\*MERGEFORMAT}{\\fldrslt IMAGE}}\\par" << endl;
   m_textStream << "}" << endl;

   DBG_RTF(m_textStream << "{\\comment (endInclDepGraph)}"    << endl)
}

void RTFGenerator::startGroupCollaboration()
{
}

void RTFGenerator::endGroupCollaboration(const DotGroupCollaboration &)
{
}

void RTFGenerator::startCallGraph()
{
   DBG_RTF(m_textStream << "{\\comment (startCallGraph)}"    << endl)
}

void RTFGenerator::endCallGraph(const DotCallGraph &g)
{
   static const QString imageExt  = Config::getEnum("dot-image-extension");

   newParagraph();

   QString fn = g.writeGraph(m_textStream, GOF_BITMAP, EOF_Rtf, m_outputDir, m_fileName, relPath, false);

   // display the file
   m_textStream << "{" << endl;
   m_textStream << rtf_Style_Reset << endl;
   m_textStream << "\\par\\pard \\qc {\\field\\flddirty {\\*\\fldinst INCLUDEPICTURE \"";
   m_textStream << fn << "." << imageExt;
   m_textStream << "\" \\\\d \\\\*MERGEFORMAT}{\\fldrslt IMAGE}}\\par" << endl;
   m_textStream << "}" << endl;
   DBG_RTF(m_textStream << "{\\comment (endCallGraph)}"    << endl)
}

void RTFGenerator::startDirDepGraph()
{
   DBG_RTF(m_textStream << "{\\comment (startDirDepGraph)}"    << endl)
}

void RTFGenerator::endDirDepGraph(const DotDirDeps &g)
{
   static const QString imageExt = Config::getEnum("dot-image-extension");

   newParagraph();

   QString fn = g.writeGraph(m_textStream , GOF_BITMAP, EOF_Rtf, m_outputDir, m_fileName, relPath, false);

   // display the file
   m_textStream << "{" << endl;
   m_textStream << rtf_Style_Reset << endl;
   m_textStream << "\\par\\pard \\qc {\\field\\flddirty {\\*\\fldinst INCLUDEPICTURE \"";
   m_textStream << fn << "." << imageExt;
   m_textStream << "\" \\\\d \\\\*MERGEFORMAT}{\\fldrslt IMAGE}}\\par" << endl;
   m_textStream << "}" << endl;

   DBG_RTF(m_textStream << "{\\comment (endDirDepGraph)}"    << endl)
}

// Tests the integrity of the result by counting brackets
void testRTFOutput(const QString &name)
{
   int bcount = 0;
   int line   = 1;
   char c;

   QFile f(name);

   if (f.open(QIODevice::ReadOnly)) {

      while (f.getChar(&c)) {

         if (c == '\\') {
            // escape char found, skip the next char

            if (! f.getChar(&c)) {
               break;
            }

         } else if (c == '{') {
            // open bracket
            bcount++;

         } else if (c == '}') {
            // close bracket
            bcount--;

            if (bcount < 0) {
               goto err;
            }

         } else if (c == '\n') {
            line++;
         }
      }
   }

   if (bcount == 0) {
      // file is good
      return;
   }

err:
   err("RTF integrity check failed at line %d of %s due to a bracket mismatch. "
       "Please notify the developers of DoxyPress at info@copperspice.com\n", line, csPrintable(name));
}

/**
 * This is an API to a VERY brittle RTF preprocessor that combines nested RTF files.
   This version replaces the infile with the new file
 */
bool RTFGenerator::preProcessFileInplace(const QString &path, const QString &name)
{
   static const QString outputDir = Config::getString("output-dir");

   QDir d(path);

   if (! d.exists()) {
      err("RTF, Output directory %s does not exist\n", csPrintable(path));
      return false;
   }

   // move to the output directory
   QString rtfDir;

   if (d.isAbsolute()) {
      rtfDir = path;

   } else {
      rtfDir = outputDir + "/" + path;

   }

   // save the original directory
   QString oldDir = QDir::currentPath();

   QDir::setCurrent(rtfDir);

   QString combinedName = rtfDir + "/combined.rtf";
   QFile outf(combinedName);

   if (! outf.open(QIODevice::WriteOnly)) {
      err("Unable to open file %s for writing, OS Error #: %d\n", csPrintable(combinedName), outf.error());

      // reset the directory to the original location
      QDir::setCurrent(oldDir);
      return false;
   }

   QTextStream outStream(&outf);
   QString mainRTFName = rtfDir + "/" + name;

   if (! preProcessFile_RTF(mainRTFName, outStream)) {
      // failed, remove the temp file
      outf.close();

      // reset the directory to the original location
      QDir::setCurrent(oldDir);
      return false;
   }

   // everything worked, remove and then rename files
   outf.close();

   QFile::remove(mainRTFName);
   QFile::rename(combinedName, mainRTFName);

   testRTFOutput(mainRTFName);

   // reset the directory to the original location
   QDir::setCurrent(oldDir);

   return true;
}

void RTFGenerator::startMemberGroupHeader(bool hasHeader)
{
   DBG_RTF(m_textStream << "{\\comment startMemberGroupHeader}" << endl)
   m_textStream << "{" << endl;

   if (hasHeader) {
      incrementIndentLevel();
   }

   m_textStream << rtf_Style_Reset << rtf_Style["GroupHeader"].m_reference;
}

void RTFGenerator::endMemberGroupHeader()
{
   DBG_RTF(m_textStream << "{\\comment endMemberGroupHeader}" << endl)
   newParagraph();
   m_textStream << rtf_Style_Reset << rtf_CList_DepthStyle();
}

void RTFGenerator::startMemberGroupDocs()
{
   DBG_RTF(m_textStream << "{\\comment startMemberGroupDocs}" << endl)
   startEmphasis();
}

void RTFGenerator::endMemberGroupDocs()
{
   DBG_RTF(m_textStream << "{\\comment endMemberGroupDocs}" << endl)
   endEmphasis();
   newParagraph();
}

void RTFGenerator::startMemberGroup()
{
   DBG_RTF(m_textStream << "{\\comment startMemberGroup}" << endl)
   m_textStream << rtf_Style_Reset << rtf_BList_DepthStyle() << endl;
}

void RTFGenerator::endMemberGroup(bool hasHeader)
{
   DBG_RTF(m_textStream << "{\\comment endMemberGroup}" << endl)
   if (hasHeader) {
      decrementIndentLevel();
   }
   m_textStream << "}";
}

void RTFGenerator::startSimpleSect(SectionTypes, const QString &file, const QString &anchor, const QString &title)
{
   DBG_RTF(m_textStream << "{\\comment (startSimpleSect)}"    << endl)

   m_textStream << "{"; // ends at endDescList
   m_textStream << "{"; // ends at endDescTitle
   startBold();
   newParagraph();

   if (! file.isEmpty()) {
      writeObjectLink(QString(), file, anchor, title);
   } else {
      docify(title);
   }

   endBold();
   m_textStream << "}";
   newParagraph();
   incrementIndentLevel();
   m_textStream << rtf_Style_Reset << rtf_DList_DepthStyle();
}

void RTFGenerator::endSimpleSect()
{
   DBG_RTF(m_textStream << "{\\comment (endSimpleSect)}"    << endl)

   m_omitParagraph = false;
   newParagraph();
   decrementIndentLevel();
   m_omitParagraph = true;
   m_textStream << "}";
}

void RTFGenerator::startParamList(ParamListTypes, const QString &title)
{
   DBG_RTF(m_textStream << "{\\comment (startParamList)}"    << endl)
   m_textStream << "{"; // ends at endParamList
   m_textStream << "{"; // ends at endDescTitle
   startBold();
   newParagraph();
   docify(title);
   endBold();
   m_textStream << "}";
   newParagraph();
   incrementIndentLevel();
   m_textStream << rtf_Style_Reset << rtf_DList_DepthStyle();
}

void RTFGenerator::endParamList()
{
   DBG_RTF(m_textStream << "{\\comment (endParamList)}"    << endl)
   newParagraph();
   decrementIndentLevel();
   m_omitParagraph = true;
   m_textStream << "}";
}

void RTFGenerator::startParameterType(bool first, const QString &key)
{
   DBG_RTF(m_textStream << "{\\comment (startParameterType)}"    << endl)

   if (! first && ! key.isEmpty()) {
      m_textStream << " " << key << " ";
   }
}

void RTFGenerator::endParameterType()
{
   DBG_RTF(m_textStream << "{\\comment (endParameterType)}"    << endl)
   m_textStream << " ";
}

void RTFGenerator::exceptionEntry(const QString &prefix, bool closeBracket)
{
   DBG_RTF(m_textStream << "{\\comment (exceptionEntry)}"    << endl)

   if (! prefix.isEmpty()) {
      m_textStream << " " << prefix << "(";

   } else if (closeBracket) {
      m_textStream << ")";
   }

   m_textStream << " ";
}

void RTFGenerator::writeDoc(DocNode *n, QSharedPointer<Definition> ctx, QSharedPointer<MemberDef> md)
{
   (void) md;

   RTFDocVisitor *visitor = new RTFDocVisitor(m_textStream, *this, ctx ? ctx->getDefFileExtension() : QString());
   n->accept(visitor);
   delete visitor;

   m_omitParagraph = true;
}

void RTFGenerator::rtfwriteRuler_doubleline()
{
   DBG_RTF(m_textStream << "{\\comment (rtfwriteRuler_doubleline)}"    << endl)
   m_textStream << "{\\pard\\widctlpar\\brdrb\\brdrdb\\brdrw15\\brsp20 \\adjustright \\par}" << endl;
}

void RTFGenerator::rtfwriteRuler_emboss()
{
   DBG_RTF(m_textStream << "{\\comment (rtfwriteRuler_emboss)}"    << endl)
   m_textStream << "{\\pard\\widctlpar\\brdrb\\brdremboss\\brdrw15\\brsp20 \\adjustright \\par}" << endl;
}

void RTFGenerator::rtfwriteRuler_thick()
{
   DBG_RTF(m_textStream << "{\\comment (rtfwriteRuler_thick)}"    << endl)
   m_textStream << "{\\pard\\widctlpar\\brdrb\\brdrs\\brdrw75\\brsp20 \\adjustright \\par}" << endl;
}

void RTFGenerator::rtfwriteRuler_thin()
{
   DBG_RTF(m_textStream << "{\\comment (rtfwriteRuler_thin)}"    << endl)
   m_textStream << "{\\pard\\widctlpar\\brdrb\\brdrs\\brdrw5\\brsp20 \\adjustright \\par}" << endl;
}

void RTFGenerator::startConstraintList(const QString &header)
{
   DBG_RTF(m_textStream << "{\\comment (startConstraintList)}"    << endl)
   m_textStream << "{"; // ends at endConstraintList
   m_textStream << "{";
   startBold();
   newParagraph();
   docify(header);
   endBold();
   m_textStream << "}";
   newParagraph();
   incrementIndentLevel();
   m_textStream << rtf_Style_Reset << rtf_DList_DepthStyle();
}

void RTFGenerator::startConstraintParam()
{
   DBG_RTF(m_textStream << "{\\comment (startConstraintParam)}"    << endl)
   startEmphasis();
}

void RTFGenerator::endConstraintParam()
{
   DBG_RTF(m_textStream << "{\\comment (endConstraintParam)}"    << endl)
   endEmphasis();
   m_textStream << " : ";
}

void RTFGenerator::startConstraintType()
{
   DBG_RTF(m_textStream << "{\\comment (startConstraintType)}"    << endl)
   startEmphasis();
}

void RTFGenerator::endConstraintType()
{
   DBG_RTF(m_textStream << "{\\comment (endConstraintType)}"    << endl)
   endEmphasis();
   m_textStream << " ";
}

void RTFGenerator::startConstraintDocs()
{
   DBG_RTF(m_textStream << "{\\comment (startConstraintDocs)}"    << endl)
}

void RTFGenerator::endConstraintDocs()
{
   DBG_RTF(m_textStream << "{\\comment (endConstraintDocs)}" << endl)
   newParagraph();
}

void RTFGenerator::endConstraintList()
{
   DBG_RTF(m_textStream << "{\\comment (endConstraintList)}" << endl)
   newParagraph();
   decrementIndentLevel();
   m_omitParagraph = true;
   m_textStream << "}";
}

void RTFGenerator::startIndexListItem()
{
   DBG_RTF(m_textStream << "{\\comment (startIndexListItem)}" << endl)
}

void RTFGenerator::endIndexListItem()
{
   DBG_RTF(m_textStream << "{\\comment (endIndexListItem)}" << endl)
   m_textStream << "\\par" << endl;
}

void RTFGenerator::startInlineHeader()
{
   DBG_RTF(m_textStream << "{\\comment (startInlineHeader)}" << endl)
   m_textStream << "{" << endl;
   m_textStream << rtf_Style_Reset << rtf_Style["Heading5"].m_reference;
   startBold();
}

void RTFGenerator::endInlineHeader()
{
   DBG_RTF(m_textStream << "{\\comment (endInlineHeader)}" << endl)
   endBold();
   m_textStream << "\\par";
   m_textStream << "}" << endl;
}

void RTFGenerator::startMemberDocSimple(bool isEnum)
{
   DBG_RTF(m_textStream << "{\\comment (startMemberDocSimple)}" << endl)

   m_textStream << "{\\par" << endl;
   m_textStream << "{" << rtf_Style["Heading5"].m_reference << endl;

   if (isEnum) {
      m_textStream << theTranslator->trEnumerationValues();

   } else {
      m_textStream << theTranslator->trCompoundMembers();
   }

   m_textStream << ":\\par}" << endl;
   m_textStream << rtf_Style_Reset << rtf_DList_DepthStyle();

   m_textStream << "\\trowd \\trgaph108\\trleft426\\tblind426"
     "\\trbrdrt\\brdrs\\brdrw10\\brdrcf15 "
     "\\trbrdrl\\brdrs\\brdrw10\\brdrcf15 "
     "\\trbrdrb\\brdrs\\brdrw10\\brdrcf15 "
     "\\trbrdrr\\brdrs\\brdrw10\\brdrcf15 "
     "\\trbrdrh\\brdrs\\brdrw10\\brdrcf15 "
     "\\trbrdrv\\brdrs\\brdrw10\\brdrcf15 " << endl;

   int i;
   int n = 3;
   int columnPos[3] = { 25, 50, 100 };

   if (isEnum) {
      columnPos[0] = 30;
      columnPos[1] = 100;
      n = 2;
   }

   for (i = 0; i < n; i++) {
      m_textStream << "\\clvertalt\\clbrdrt\\brdrs\\brdrw10\\brdrcf15 "
        "\\clbrdrl\\brdrs\\brdrw10\\brdrcf15 "
        "\\clbrdrb\\brdrs\\brdrw10\\brdrcf15 "
        "\\clbrdrr \\brdrs\\brdrw10\\brdrcf15 "
        "\\cltxlrtb "
        "\\cellx" << (rtf_pageWidth * columnPos[i] / 100) << endl;
   }

   m_textStream << "\\pard \\widctlpar\\intbl\\adjustright" << endl;
}

void RTFGenerator::endMemberDocSimple(bool isEnum)
{
   (void) isEnum;

   DBG_RTF(m_textStream << "{\\comment (endMemberDocSimple)}" << endl)
   m_textStream << "}" << endl;
}

void RTFGenerator::startInlineMemberType()
{
   DBG_RTF(m_textStream << "{\\comment (startInlineMemberType)}" << endl)
   m_textStream << "{\\qr ";
}

void RTFGenerator::endInlineMemberType()
{
   DBG_RTF(m_textStream << "{\\comment (endInlineMemberType)}" << endl)
   m_textStream << "\\cell }";
}

void RTFGenerator::startInlineMemberName()
{
   DBG_RTF(m_textStream << "{\\comment (startInlineMemberName)}" << endl)
   m_textStream << "{";
}

void RTFGenerator::endInlineMemberName()
{
   DBG_RTF(m_textStream << "{\\comment (endInlineMemberName)}" << endl)
   m_textStream << "\\cell }";
}

void RTFGenerator::startInlineMemberDoc()
{
   DBG_RTF(m_textStream << "{\\comment (startInlineMemberDoc)}" << endl)
   m_textStream << "{";
}

void RTFGenerator::endInlineMemberDoc()
{
   DBG_RTF(m_textStream << "{\\comment (endInlineMemberDoc)}" << endl)
   m_textStream << "\\cell }{\\row }" << endl;
}

void RTFGenerator::writeLineNumber(const QString &, const QString &fileName, const  QString &, int line)
{
  static const bool rtfHyperlinks = Config::getBool("rtf-hyperlinks");

  m_doxyCodeLineOpen = true;
  QString lineNumber = QString("%1").formatArg(line, 5, 10, QChar('0'));

  if (m_prettyCode) {
    if (! fileName.isEmpty() && ! m_sourceFileName.isEmpty() && rtfHyperlinks) {
      QString lineAnchor = QString("_l%1").formatArg(line, 5, 10, '0');
      lineAnchor.prepend(stripExtensionGeneral(m_sourceFileName, ".rtf"));

      m_textStream << "{\\bkmkstart ";
      m_textStream << rtfFormatBmkStr(lineAnchor);
      m_textStream << "}";
      m_textStream << "{\\bkmkend ";
      m_textStream << rtfFormatBmkStr(lineAnchor);
      m_textStream << "}" << endl;
    }

    m_textStream << lineNumber << " ";

  } else {
    m_textStream << line << " ";
  }

  m_col = 0;
}

void RTFGenerator::startCodeLine(bool)
{
  m_doxyCodeLineOpen = true;
  m_col = 0;
}

void RTFGenerator::endCodeLine()
{
   if (m_doxyCodeLineOpen) {
     lineBreak();
   }

   m_doxyCodeLineOpen = false;
}

void RTFGenerator::startLabels()
{
}

void RTFGenerator::writeLabel(const QString &l, bool isLast)
{
   m_textStream << "{\\f2 [" << l << "]}";

   if (! isLast) {
      m_textStream << ", ";
   }
}

void RTFGenerator::endLabels()
{
}

void RTFGenerator::startFontClass(const QString &name)
{
   int id = 2;

   if (name == "keyword")  {
      id = 17;

   } else if (name == "keywordtype") {
      id = 18;

   } else if (name == "keywordflow") {
      id = 19;

   } else if (name == "comment") {
      id = 20;

   } else if (name == "preprocessor") {
      id = 21;

   } else if (name == "stringliteral") {
      id = 22;

   } else if (name == "charliteral") {
      id = 23;

   }

   m_textStream << "{\\cf" << id << " ";
}

void RTFGenerator::endFontClass()
{
   m_textStream << "}";
}

